#include "protocol.h"

// SupFrames
SupFrame SET={FLAG,ADDR_TRANS,CTRL_SET,(ADDR_TRANS^CTRL_SET),FLAG};

SupFrame UA_REC={FLAG,ADDR_RECIE,CTRL_UA,ADDR_RECIE^CTRL_UA,FLAG};
SupFrame UA_TRA={FLAG,ADDR_TRANS,CTRL_UA,ADDR_TRANS^CTRL_UA,FLAG};

SupFrame DISC_TRA={FLAG,ADDR_TRANS,CTRL_DISC,ADDR_TRANS^CTRL_DISC,FLAG};
SupFrame DISC_REC={FLAG,ADDR_RECIE,CTRL_DISC,ADDR_RECIE^CTRL_DISC,FLAG};

SupFrame RR0={FLAG,ADDR_RECIE,CTRL_RR_S0,ADDR_RECIE^CTRL_RR_S0,FLAG};
SupFrame RR1={FLAG,ADDR_RECIE,CTRL_RR_S1,ADDR_RECIE^CTRL_RR_S1,FLAG};
SupFrame REJ0={FLAG,ADDR_RECIE,CTRL_REJ_S0,ADDR_RECIE^CTRL_REJ_S0,FLAG};
SupFrame REJ1={FLAG,ADDR_RECIE,CTRL_REJ_S1,ADDR_RECIE^CTRL_REJ_S1,FLAG};

struct termios oldtio,newtio;
unsigned int messageStatus = 0,fd=0,flag=0,conta=0,role;
enum messageType toSend = _SET, toGet=_SET;
LinkLayer lin;
char supFrameRead[5];//for reading control packets
char * infoFrame;//to save last info buffer
int asd;

void atende(){// atende alarme
  unsigned int res;
  printf("timeout exceeded. alarm # %d\n", conta);
  if(conta < lin.numRetransmissions){
    if(toSend != _S0 && toSend != _S1){
    	printf("Resending control packet.......\n");
    	unsigned int b;
    	for(b=0;b<5;b++)
    		printf("SUP FRAME[%d] = %x\n",b,supFrameRead[b]);
    }
  switch(toSend){      
    case _SET: 
	  res = tcflush(fd, TCIFLUSH);
	  if(res < 0 ) perror("tcflush");
      res = write(fd,&SET,5);
      if(res <0){perror("write"); exit(-1);      }
      break;
    case _UA:  //so considerado caso transmissor pq receptor nao tem alarme
	  res = tcflush(fd, TCIFLUSH);
	  if(res < 0 ) perror("tcflush");
      res = write(fd,&UA_TRA,5);
      if(res <0){perror("write");exit(-1);      }
      break;
    case _DISC: //so considerado caso transmissor pq receptor nao tem alarme
	  res = tcflush(fd, TCIFLUSH);
	  if(res < 0 ) perror("tcflush");
      res = write(fd,&DISC_TRA,5);
      if(res <0){perror("write");exit(-1);      }
      break;
    case _S0:
    case _S1:
     printf("Resending information packet.......\n");
     unsigned int b;
     for(b=0;b<asd;b++)
    		printf("%x|",infoFrame[b]);
     printf("\n");
	  res = tcflush(fd, TCIFLUSH);
	  if(res < 0 ) perror("tcflush");
     res = write(fd,infoFrame,asd);
     if(res <0){perror("write");exit(-1);      }
     break;
    case _RR_S0:
    case _RR_S1:
    case _REJ_S0:
    case _REJ_S1:
    default:
      break;
    }
    alarm(lin.timeout);
    flag=1;
    conta++;
  }else{
    printf("Number of retransmissions exceeded. Exiting.....\n");
    exit(-1);
  }
}

void receiveSET(char byte){
  switch(messageStatus){
    case 0:
      if(byte == FLAG) {
	    messageStatus=1;//printf("Got FLAG\n");
      }
      else {
    	messageStatus = 0;printf("SET:error input\n");
      }
      break;
    case 1:
      if(byte == ADDR_TRANS) {
	    messageStatus=2;//printf("Got ADDR_TRANS\n");
      }
      else {
	    messageStatus = 0;printf("SET:error input\n");
      }
      break;
    case 2: 
      if(byte == CTRL_SET) {
	    messageStatus=3;//printf("got CTRL_SET\n");
      }
      else {
	    messageStatus = 0;printf("SET:error input\n");
      }
      break;
    case 3: 
      if(byte == (ADDR_TRANS^CTRL_SET)) {
	    messageStatus=4;//printf("got bcc\n");
      }
      else {
	    messageStatus = 0;printf("SET:error input\n");
      }
      break;
    case 4:
      if(byte == FLAG){
       messageStatus=5;      
	    printf("Received SET.\n");
      }
      else {
	    messageStatus = 0;
	    printf("4:error input\n");
      }
  }
}

void receiveUA(char msg, unsigned int role){
  switch(messageStatus){
    case 0://nao ha nada, quer-se FLAG
      if(msg == FLAG){
        messageStatus=1;
        //printf("UA: got FLAG\n");
      }
      break;
    case 1://temos FLAG quer-se ADDR_RECIE
      if((msg == ADDR_RECIE && role == TRANS)
       ||(msg == ADDR_TRANS && role == RECIE)){
        messageStatus=2;
        //printf("UA: got ADDR_RECIE\n");
      }else if(msg == FLAG){
        messageStatus=1;
        //printf("UA: got FLAG\n");
      }else{
        messageStatus=0;
        printf("UA: error input\n");
      }
      break;
    case 2://FLAG+ADDR_RECIE, quer-se CTRL_UA
      if(msg == CTRL_UA){
        messageStatus=3;
        //printf("UA: got CTRL_UA\n");
      }else if(msg == FLAG){
        messageStatus=1;
        //printf("UA: got FLAG\n");
      }else{
        messageStatus=0;
        printf("UA: error input\n");
      }
      break;
    case 3://FLAG+ADDR_RECIE+CTRL_UA, quer-se (ADDR_RECIE ^ CTRL_UA)
      if((msg == (ADDR_RECIE ^ CTRL_UA) && role == TRANS) ||
         (msg == (ADDR_TRANS ^ CTRL_UA) && role == RECIE)){
        messageStatus=4;
        //printf("UA: got correct bcc\n");
      }else if(msg == FLAG){
        messageStatus=1;
        //printf("UA: got FLAG\n");
      }else{
        messageStatus=0;
        printf("UA: error input\n");
      }
      break;
    case 4://FLAG+ADDR_RECIE+CTRL_UA+(ADDR_RECIE ^ CTRL_UA), quer-se FLAG
      if(msg == FLAG){
        messageStatus=5;
        printf("UA successfully received\n");
      }else{
        messageStatus=0;
        printf("UA: error input\n");
      }
      break;
    default://estoiro
      printf("UA: non-predicted input, error\n");
      break;
  }
}

void receiveDISC(char msg){
	switch(messageStatus){
	case 0:
		if(msg == FLAG){
			messageStatus=1;
			printf("Got FLAG\n");
		}else{
			messageStatus=0;
			printf("FLAG failed\n");
		}
		break;
	case 1:
		if(msg == ADDR_RECIE){
			messageStatus=2;
			printf("Got ADDR_TRANS\n");
		}else if(msg != FLAG){
			messageStatus=0;
			printf("ADDR failed\n");
		}
		break;
	case 2:
		if(msg == CTRL_DISC){
			messageStatus=3;
			printf("Got CTRL_DISC\n");
		}else if(msg == FLAG){
			messageStatus = 1;
		}else{
			messageStatus=0;
			printf("CTRL_DISC failed\n");
		}
		break;
	case 3:
		if(msg == (ADDR_RECIE^CTRL_DISC)){
			messageStatus=4;
			printf("Got BBC\n");
		}else if(msg == FLAG){
			messageStatus = 1;
		}else{
			messageStatus=0;
			printf("BCC failed\n");
		}
		break;
	case 4:
		if(msg == FLAG){
			messageStatus=5;
			printf("Got FLAG\n");
		}else{
			messageStatus=0;
			printf("FLAG failed\n");
		}
		break;
	default://estoiro
      printf("non-predicted input, error\n");
      break;
	}
}

/*currType identifica o tipo da mensagem; se comum a ambos _NONE, RR[sequenceNum] quando encontra CTRL do RR, REJ[sequenceNum] se encontrar CTRL do REJ*/
void receiveRR_REJ(char msg, unsigned int sequenceNum, enum messageType * currType){
  switch(messageStatus){
    case 0://nao ha nada, quer-se FLAG
      if(msg == FLAG){
        messageStatus=1;
        //printf("got FLAG\n");
        (*currType)=_NONE;
      }else{ messageStatus=0; printf("First Flag FAIL\n");}
      break;
    case 1://temos FLAG quer-se ADDR_RECIE
      if(msg == ADDR_RECIE){
      	messageStatus=2;
         //printf("got ADDR_RECIE\n");
  		   (*currType)= _NONE;
      }else if(msg == FLAG){
      	messageStatus=1;
        printf("RR_1:got FLAG\n");
        	(*currType)=_NONE;
      }else{
      	messageStatus=0;
      	printf("RR_1:error input\n");
      	(*currType)= _NONE;
      }
      break;
    case 2://FLAG+ADDR_RECIE, quer-se CTRL
      if(msg == CTRL_RR_S0 && sequenceNum == 0){
        messageStatus=3;
        //printf("got CTRL_RR_S0\n");
        (*currType)= _RR_S0;
      }else if(msg == CTRL_RR_S1 && sequenceNum == 1){
      	messageStatus=3;
        //printf("got CTRL_RR_S1\n");
        (*currType)=_RR_S1;
      }else if(msg == CTRL_REJ_S0 && sequenceNum == 0){
        messageStatus=3;
        //printf("got CTRL_RR_S0\n");
        (*currType)= _REJ_S0;
      }else if(msg == CTRL_REJ_S1 && sequenceNum == 1){
      	messageStatus=3;
        //printf("got CTRL_RR_S1\n");
        (*currType)=_REJ_S1;
      }else if(msg == FLAG){
        messageStatus=1;
        printf("RR_2:got FLAG\n");
        (*currType)=_NONE;
      }else{
        messageStatus=0;
        printf("RR_2:error input\n");
        (*currType)=_NONE;
      }
      break;
    case 3://FLAG+ADDR_RECIE+CTRL_UA, quer-se (ADDR_RECIE ^ CTRL_UA)
      if((msg == (ADDR_RECIE ^ CTRL_RR_S0)) && sequenceNum == 0 && (*currType) == _RR_S0){
        messageStatus=4;
        //printf("got correct bcc\n");
      }else if((msg == (ADDR_RECIE ^ CTRL_RR_S1)) && sequenceNum == 1 && (*currType) == _RR_S1)
      	messageStatus=4;
      else if((msg == (ADDR_RECIE ^ CTRL_REJ_S0)) && sequenceNum == 0 && (*currType) == _REJ_S0)
			messageStatus=4;
      else if((msg == (ADDR_RECIE ^ CTRL_REJ_S1)) && sequenceNum == 1 && (*currType) == _REJ_S1)
			messageStatus=4;
      else if(msg == FLAG){
      	messageStatus=1;
      	printf("RR_3:got FLAG\n");
      	(*currType)=_NONE;
      }else{
      	messageStatus=0;
      	printf("RR_4:error input\n");
      	(*currType)=_NONE;
      }
      break;
    case 4://FLAG+ADDR_RECIE+CTRL_UA+(ADDR_RECIE ^ CTRL_UA), quer-se FLAG/
      if(msg == FLAG){
      	messageStatus = 5;
     	//printf("got second FLAG\n");
		if((*currType) == _RR_S0 || (*currType) == _RR_S1){
         	printf("RR_S%d successfully received\n",sequenceNum);
        }else if((*currType) == _REJ_S0 || (*currType) == _REJ_S1){
         	printf("REJ_S%d successfully received\n",sequenceNum);
	    }
      }else{
         messageStatus=0;
		(*currType)=_NONE;
		printf("RR_4:error input\n");
      }
      break;
    default://estoiro
      printf("non-predicted input, error\n");
      break;
  }
}

char* destuff(char* buffer, unsigned int start, unsigned int len, unsigned int * disLen){
  char* data;
  data=malloc(len-start);
  unsigned int i=start;
  int j = 0;
  for (i=start;i<len;++i) {
	  if (buffer[i]==ESCAPE && buffer[i+1]==FLAG_SUB) {
		  data[j]=FLAG;
		  ++i;
	   }else if(buffer[i]==ESCAPE && buffer[i+1]==ESC_SUB){    	
		  data[j]=ESCAPE;
		  ++i;
	   }else{data[j]=buffer[i];}
  }
  (*disLen) = j;
  return data;
}

char* stuff(char* buffer, unsigned int oldLen, unsigned int* newLen, unsigned int* bcc){
  (*bcc)=0;
  unsigned int skip=0;//quantos
  char* data;
  data=malloc(oldLen*2);
  unsigned int i=0;
  for (i=0;i<oldLen;++i) {
    (*bcc)=(*bcc)^buffer[i];
    if (buffer[i]==FLAG) {
      data[i+skip]=ESCAPE;
      skip++;
      data[i+skip]=FLAG_SUB;
    }else if(buffer[i]==ESCAPE){
      data[i+skip]=ESCAPE;
      skip++;
      data[i+skip]=ESC_SUB;
    }else {
      data[i+skip]=buffer[i];
    }
  }
  (*newLen)=oldLen+skip;
  return data;
}

void configureLinkLayer(LinkLayer * l){
  strcpy(&lin.port[0],l->port);
  lin.baudRate = l->baudRate;
  lin.sequenceNumber = l->sequenceNumber;
  lin.timeout = l->timeout;
  lin.numRetransmissions = l->numRetransmissions;
  lin.maxSize = l->maxSize;
}

//---------------------------------------------------------------------------------------------------//

unsigned int llopen(char * porta, unsigned int status){
  role=status;
  fd = open(porta, O_RDWR | O_NOCTTY );
  if (fd <0) {
    perror(porta); 
    return -1; 
  }

  if ( tcgetattr(fd,&oldtio) == -1) { /* save current port settings */
    perror("tcgetattr");
    return -1;
  }

  bzero(&newtio, sizeof(newtio));
  newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
  newtio.c_iflag = IGNPAR;
  newtio.c_oflag = OPOST;

  /* set input mode (non-canonical, no echo,...) */
  newtio.c_lflag = 0;
  newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused - now used, timeout */
  newtio.c_cc[VMIN]     = 1;   /* blocking read until 5 chars received - no blocking*/

  int res = tcflush(fd, TCIFLUSH);if(res < 0) perror("tcflush");

  if ( tcsetattr(fd,TCSANOW,&newtio) == -1) {
    perror("tcsetattr");
    return -1;
  }
  
  //-------------------------------------------------------------------------------------------
  char array[5];
  if(role==TRANS){//transmitter  
    unsigned int res;
    printf("Has transmitter role\n");
    (void) signal(SIGALRM, atende);
    printf("SET sent.\n");
    toSend = _SET;
    res = tcflush(fd, TCIFLUSH);
 	if(res < 0 ) perror("tcflush");
    res = write(fd,&SET,5);
    alarm(lin.timeout);
    printf("Started alarm\n");
  
    messageStatus=0;
    printf("Waiting for UA..............\n");
    toGet = _UA;
    unsigned int d;
	messageStatus=0;    
    for(d = 0; d < 5; d++){
        res = read(fd, &array[d], 1);
        if(res <= 0) perror("read");
        supFrameRead[d] = array[d];
        receiveUA(array[d],role);
        if(messageStatus > 4) break;
    }
    if(messageStatus < 4){
      printf("error receiving UA. Closing....\n");
      messageStatus=0;
      conta=0;
      return -1;
    }else{messageStatus = 0;}
  }else if(role==RECIE){//receiver
    printf("Has receiver role\nWaiting for SET....\n");
    unsigned int i,res;
    messageStatus=0;
    for(i = 0; i < 5; i++){
      res = read(fd, &array[i], 1);
      if(res < 0) perror("read");
      supFrameRead[i]=array[i];
      receiveSET(array[i]);
      if(messageStatus > 4) break;
    }
    if(messageStatus > 4){ 
      printf("SET received.\nSent UA......\n");
      alarm(0);
      conta=0;
      res = tcflush(fd, TCIFLUSH);
 	  if(res < 0 ) perror("tcflush");
      res = write(fd, &UA_REC, 5);
      if(res < 0 ) perror("write");
      messageStatus = 0;    
    }else{
      printf("error receiving SET. Closing....\n");
      return -1;
    }
  }else{
    return -1;
  }
  
  return fd;
}

void handleReceiverDisconnect(unsigned int fd){
	printf("Sending return disconnect................\n");
	unsigned int res;
	res = tcflush(fd, TCIFLUSH);
	if(res < 0 ) perror("tcflush");
		
	res = write(fd,&DISC_REC,5);
	if(res <0) perror("write");
	
	printf("Waiting for UA.........\n");
	unsigned int i;
	for(i=0;i<5;i++){
		res = read(fd,&supFrameRead[i],1);
		if(res <0) perror("read");
		receiveUA(supFrameRead[i],RECIE);
		if(messageStatus > 4) break;
	}	
	
	if(messageStatus > 4){ 
      printf("UA received.\nClosing connection......\n");
      llclose(fd);   
    }else{
      printf("error receiving UA. Closing....\n");
    }
	return;
}
void llread(unsigned int fd, char * buffer, unsigned int len){
	//char * dataStuff = malloc(len*sizeof(char));	
	char * dataDestuff = malloc(len*sizeof(char));
	unsigned int i, destuffedSize;
	char bccTemp = 0;

    //destuff dos dados
	dataDestuff = destuff(buffer,4,len-1, &destuffedSize);
	char bcc2 = dataDestuff[destuffedSize - 1];
	
	--destuffedSize;
	unsigned int o;
	for(o=0; o < destuffedSize; o++){
		bccTemp = bccTemp^dataDestuff[o];
	}
	
	printf("deSize=%d | bcc temp = %x | bcc = %x\n", destuffedSize, bccTemp, bcc2);

	unsigned int res;
	//verificação da PARIDADE
	if(bccTemp==bcc2) {
		if(lin.sequenceNumber== 0){
			res = tcflush(fd, TCIFLUSH);
			if(res < 0 ) perror("tcflush");
		
			res=write(fd,&RR0,5);		
			printf("Sent RR0.\n");
			if(res < 0) perror("write"); 
			
			lin.sequenceNumber = 1;
		}else{ 
			res = tcflush(fd, TCIFLUSH);
			if(res < 0 ) perror("tcflush");
		
 			res=write(fd,&RR1,5);
 			printf("Sent RR1.\n");
			if(res < 0) perror("write");
			
			lin.sequenceNumber = 0;
		} 
	}else {
		if(lin.sequenceNumber == 0){
			res = tcflush(fd, TCIFLUSH);
			if(res < 0 ) perror("tcflush");
		
			res=write(fd,&REJ0,5);
			printf("Sent REJ0.\n");
			if(res < 0) perror("write"); 

		}else{
			res = tcflush(fd, TCIFLUSH);
			if(res < 0 ) perror("tcflush");
					
			 
 			res=write(fd,&REJ1,5);
 			printf("Sent REJ1.\n");
			if(res < 0) perror("write");
		}
	}	
}

unsigned int llwrite(unsigned int fd, char * buffer, unsigned int length){
	char* stuffed_data;//stuffed buffer (passed as param)
	unsigned int newLen=0, bcc=0;
	unsigned int d,res;
	enum messageType currType = _NONE;

	if (length>0) {
		stuffed_data=stuff(buffer,length,&newLen,&bcc);
    	//meter flags 
    	int packetSize = 0;
    	if(bcc == FLAG || bcc == ESCAPE){
    		packetSize = newLen+7;
    	}else{
    		packetSize = newLen+6;
    	}
    	
    	infoFrame=malloc((packetSize)*sizeof(char));
    	
    	infoFrame[0]=FLAG;
    	infoFrame[1]=ADDR_TRANS;
    	if(lin.sequenceNumber == 0){
	    	infoFrame[2]=CTRL_S0;
	    	infoFrame[3]=(ADDR_TRANS^CTRL_S0);
	   }else{
	   	infoFrame[2]=CTRL_S1;
	   	infoFrame[3]=(ADDR_TRANS^CTRL_S1);
	   }
    	
    	//meter stuffed data
    	unsigned int i;
    	for (i=0;i<=newLen;++i) {
   	   infoFrame[4+i]=stuffed_data[i];
   	 }
   	if(bcc == FLAG){
   		infoFrame[packetSize-3]=ESCAPE;
   		infoFrame[packetSize-2]=FLAG_SUB;
   	}else if(bcc == ESCAPE){
   		infoFrame[packetSize-3]=ESCAPE;
   		infoFrame[packetSize-2]=ESC_SUB;
   	}else{
   		infoFrame[packetSize-2]=bcc;
   	}
   	printf("LALALALALALALALALALALA %x|%x\n",infoFrame[packetSize-3],infoFrame[packetSize-2]);
    	
    	infoFrame[packetSize-1]=FLAG;
    	//send buffer
    
   	//imprimir o que vai enviar
   	for(i = 0; i < packetSize; i++){
    	 printf("%x|", infoFrame[i]);
    	}
    	printf("\n");
    	res = tcflush(fd, TCIFLUSH);
		if(res < 0 ) perror("tcflush");
    	res = write(fd,infoFrame,newLen+6);
    	
    	asd=newLen+6;

    	alarm(lin.timeout); 
    
    	//receber RR_S[sequenceNumber]
   	toGet = lin.sequenceNumber == 0 ? _RR_S0 : _RR_S1;  
   	toSend = lin.sequenceNumber == 0 ? _S0 : _S1;
    	      
      char array[5];messageStatus=0;conta=0;
    	for(d = 0; d < 5; d++){
     		res = read(fd, &array[d], 1);
     		if(res < 0){ perror("read"); return -1;}
     		supFrameRead[d] = array[d];
     		//printf("RR: c = %x | msg = %d\n", array[d], messageStatus);
     		receiveRR_REJ(array[d],lin.sequenceNumber,&currType);
   	}
    
  	 	if(messageStatus > 4){
  	 		//printf("seq = %d | curr = %d\n", lin.sequenceNumber, currType);
  	 		if(currType == _RR_S0 && lin.sequenceNumber == 0){
  	   	      lin.sequenceNumber = 1;
  	 		  printf("é válido\n");
			}else if(currType == _RR_S1 && lin.sequenceNumber == 1){
			  lin.sequenceNumber = 0;
			}else if((currType == _REJ_S0 && lin.sequenceNumber == 0) || (currType == _REJ_S1 && lin.sequenceNumber == 1)){
			  res = tcflush(fd, TCIFLUSH);
			  if(res < 0 ) perror("tcflush");
      		res = write(fd,infoFrame,newLen+6);
      		alarm(lin.timeout);
      	}else{printf("RR/REJ error\n");}
      	  messageStatus=0;conta=0;
      	  alarm(0);
		}
   	return newLen+6;
   	
   }else{
		printf("length 0 => send disconnect request\n");
		sleep(1);
		toGet = _DISC;  
		toSend = _DISC;
		res = tcflush(fd, TCIFLUSH);
		if(res < 0 ) perror("tcflush");
		unsigned int res = write(fd, &DISC_TRA, 5);
		if(res < 0) perror("write");
		printf("DISC sent.\nWaiting for DISC_REC......\n");
		
		char arr[5];		
		messageStatus = 0;
		
		unsigned int a;
		for(a = 0; a < 6; a++){
			res = read(fd, &arr[a], 1);
			if(res < 0) perror("read");
			supFrameRead[a] = arr[a];
			receiveDISC(arr[a]);
			if(messageStatus > 4) break;
		}
		toSend = _UA;
		if(messageStatus > 4){
			printf("DISC received.\nSent UA. Closing......\n");
			res = tcflush(fd, TCIFLUSH);
			if(res < 0 ) perror("tcflush");
			res = write(fd, &UA_TRA, 5);
			messageStatus = 0;
			alarm(0);
			llclose(fd);
			return 0;
		}else{
			printf("DISC failed.\nClosing......\n");
			return -1;
		}
	}
	return 3;
}

unsigned int llclose(unsigned int fd){
  sleep(1);
  if ( tcsetattr(fd,TCSANOW,&oldtio) == -1) {
    perror("tcsetattr");
    exit(-1);
  }
  close(fd);
  return 0;
}
